use alloc::{
    collections::BTreeMap,
    string::{String, ToString},
    sync::Arc,
    vec::Vec,
};
use spin::RwLock;
use syscall::{
    data::EventStruct,
    scheme::{FileInfo, KernelScheme, SchemeRef, SchemeTy},
};

use crate::{context::schedule, ref_to_mut};

use super::{mount_to, op::kernel_open, ROOT_SCHEME};

pub static mut INIT_PROC_PID: usize = 0;

pub fn send_event(event: &mut EventStruct) -> usize {
    let init_proc_scheme =
        kernel_open(alloc::format!("/proc/{}", unsafe { INIT_PROC_PID })).unwrap();

    init_proc_scheme.read().write_at(0, event);

    let mut ret_buf = alloc::vec![0u8; 9];
    while ret_buf[0] != 0xFF {
        init_proc_scheme.read().read_at(0, &mut ret_buf);
        schedule();
    }
    let result = ((ret_buf[1] as usize) << 56)
        | ((ret_buf[2] as usize) << 48)
        | ((ret_buf[3] as usize) << 40)
        | ((ret_buf[4] as usize) << 32)
        | ((ret_buf[5] as usize) << 24)
        | ((ret_buf[6] as usize) << 16)
        | ((ret_buf[7] as usize) << 8)
        | (ret_buf[8] as usize);

    return result;
}

pub struct UserScheme {
    schemes: BTreeMap<String, SchemeRef>,
    path: String,
    open_path: String,
}

impl UserScheme {
    pub fn new() -> SchemeRef {
        let scheme = Arc::new(RwLock::new(Self {
            schemes: BTreeMap::new(),
            path: String::new(),
            open_path: String::new(),
        }));
        scheme.write().schemes.insert(".".into(), scheme.clone());
        scheme
    }

    pub fn init() {
        let new_scheme = Self::new();

        mount_to(
            new_scheme.clone(),
            ROOT_SCHEME.lock().clone(),
            "user".to_string(),
        );
    }
}

impl KernelScheme for UserScheme {
    fn when_mounted(&mut self, path: String, father: Option<SchemeRef>) {
        self.path.clear();
        self.path.push_str(path.as_str());
        if let Some(father) = father {
            self.schemes.insert("..".into(), father.clone());
        }
    }

    fn when_umounted(&mut self) {
        for (name, node) in self.schemes.iter() {
            if name != "." && name != ".." {
                node.write().when_umounted();
            }
        }
    }

    fn mount(&mut self, node: SchemeRef, name: String) {
        self.schemes.insert(name, node);
    }

    fn get_path(&self) -> String {
        self.path.clone()
    }

    fn open(&self, name: String) -> Option<SchemeRef> {
        if let Some(schemes) = self.schemes.get(&name).cloned() {
            return Some(schemes);
        }
        if name.len() <= 5 {
            ref_to_mut(self).open_path = "/".to_string();
            return None;
        }
        ref_to_mut(self).open_path = "/".to_string() + &(name[5..name.len()]);
        None
    }

    fn read_at(&self, offset: usize, buf: &mut [u8]) -> usize {
        let mut e_struct = EventStruct {
            cmd: 1,
            magic: 0x20091103,
            arg1: self.open_path.as_ptr() as usize,
            arg2: self.open_path.len(),
            arg3: offset,
            arg4: buf.as_mut_ptr() as usize,
            arg5: buf.len(),
            arg6: 0,
        };
        return send_event(&mut e_struct);
    }

    fn write_at(&self, offset: usize, buf: &[u8]) -> usize {
        let mut e_struct = EventStruct {
            cmd: 2,
            magic: 0x20091103,
            arg1: self.open_path.as_ptr() as usize,
            arg2: self.open_path.len(),
            arg3: offset,
            arg4: buf.as_ptr() as usize,
            arg5: buf.len(),
            arg6: 0,
        };
        return send_event(&mut e_struct);
    }

    fn size(&self) -> usize {
        let mut e_struct = EventStruct {
            cmd: 3,
            magic: 0x20091103,
            arg1: self.open_path.as_ptr() as usize,
            arg2: self.open_path.len(),
            arg3: 0,
            arg4: 0,
            arg5: 0,
            arg6: 0,
        };
        return send_event(&mut e_struct);
    }

    fn scheme_type(&self) -> SchemeTy {
        SchemeTy::Dir
    }

    fn list(&self) -> alloc::vec::Vec<FileInfo> {
        let mut list_path = self.open_path.clone();
        let filename_and_path = list_path.clone();
        let filename_at_path = filename_and_path.split("/").last().unwrap();
        list_path.remove_matches(filename_at_path);
        let mut e_struct = EventStruct {
            cmd: 4,
            magic: 0x20091103,
            arg1: list_path.as_ptr() as usize,
            arg2: list_path.len(),
            arg3: 0,
            arg4: 0,
            arg5: 0,
            arg6: 0,
        };
        let ret_struct_ptr = send_event(&mut e_struct);

        let vec_ptr = unsafe { (ret_struct_ptr as *const u64).read() };
        let vec_len = unsafe { (ret_struct_ptr as *const usize).add(1).read() };
        let vec_cap = unsafe { (ret_struct_ptr as *const usize).add(2).read() };
        let vec = unsafe { Vec::from_raw_parts(vec_ptr as *mut FileInfo, vec_len, vec_cap) };

        vec
    }
}
